import fudge
from fudge import events, models
from fudge.types import Matrix3, Vector2, Vector3, Vector4, Matrix4
from fudge.renderers import DeferredRenderer
from fudge.renderers.deferred import Light, MAX_LIGHTS
from fudge.resources import Texture
from fudge.physics import RigidModel, StaticModel, Geom, Physics, Joint, RagDoll, Mass, EmptyMesh
from fudge.core import PI2
from fudge.renderers import lights as LIGHTS
import fudge.importers as importers
import sys
import ode
import random
import math
from fudge.core import *

from copy import deepcopy

RAD2DEG = 180 / math.pi

# initialize items
e = fudge.Engine(sys.argv, renderer = DeferredRenderer(debug_mode = False))



# run settings
settings = events.State.Create(
    AO = 1,
    AO_ONLY = -1,
    ALL_LIGHTS = 1,
    EDGE = 1,
    NMAP = 1,
    RELIEF = 1,
    distance = -10.0,
    DOF = 1,
    bloom = 1,
    left = 0,
    up = 0,
    w = 0,
    a = 0,
    s = 0,
    d = 0,
    light_strength = 1.0,
    light_dist = 2.0,
    mouse = None,
    fwd = Vector3(0.0, 0.0, 1.0),
    strf = Vector3(1.0, 0.0, 0.0),
    jump = False
)
state = settings

# Hack - pass settings into the DeferredRenderer - Not used
fudge.Renderer()._renderEngine.settings = settings

# call this after the previous line
w = fudge.Engine().getWindow()

# load textures/materials
wood_tex = DeferredRenderer.LoadAllTextures('textures/wood_.tga', 'dns')
wall_tex = DeferredRenderer.LoadAllTextures("textures/wall_.tga", 'dns')

scene = e.scene


scene2 = models.Empty()
scene.addModel(scene2)


# dynamic elements    
character = RigidModel(mass = Mass.Sphere(1., 1.), mesh= EmptyMesh(), geom = Geom.Capsule(0.1, 0.5), position = Vector3(0.0, 1.0, 0.0))
scene2.addModel(character)

textures= [wall_tex, wood_tex]

def cube(x, z, y = 0.0, walls = [1,1,1,1,1,1], tex = [1, 0, 0, 0, 0, 1]):
    x *= 2.0
    y *= 2.0
    z *= 2.0
    if walls[0]:
        scene2.addModel(StaticModel(mesh = models.Plane.Mesh(1.0), position = Vector3(x,y,z), geom = Geom.Cuboid(2.0, 0.05, 2.0), textures = textures[tex[0]]))
    if walls[1]:
        scene2.addModel(StaticModel(mesh = models.Plane.Mesh(1.0), position = Vector3(0.0 + x, 1.0 + y, -1.0 + z), geom = Geom.Cuboid(2.0, 2.0, 0.05), rotation = Matrix3.RotationX(1.57), textures = textures[tex[1]]))
    if walls[2]:
        scene2.addModel(StaticModel(mesh = models.Plane.Mesh(1.0), position = Vector3(1.0 + x, 1.0 + y, 0.0 + z), geom = Geom.Cuboid(0.05, 2.0, 2.0), rotation = Matrix3.RotationX(1.57) * Matrix3.RotationZ(1.57), textures = textures[tex[2]]))
    if walls[3]:
        scene2.addModel(StaticModel(mesh = models.Plane.Mesh(1.0), position = Vector3(0.0 + x, 1.0 + y, 1.0 + z), geom = Geom.Cuboid(2.0, 2.0, 0.05), rotation = Matrix3.RotationX(-1.57), textures = textures[tex[3]]))
    if walls[4]:
        scene2.addModel(StaticModel(mesh = models.Plane.Mesh(1.0), position = Vector3(-1.0 + x, 1.0 + y, 0.0 + z), geom = Geom.Cuboid(0.05, 2.0, 2.0), rotation = Matrix3.RotationX(1.57) * Matrix3.RotationZ(-1.57), textures = textures[tex[4]]))
    if walls[5]:
        scene2.addModel(StaticModel(mesh = models.Plane.Mesh(1.0), position = Vector3(0.0 + x, 2.0 + y, 0.0 + z), geom = Geom.Cuboid(2.0, 0.05, 2.0), rotation = Matrix3.RotationX(3.14), textures = textures[tex[5]]))


# buildmap
cube(0, 0, walls = [1, 0, 1, 1, 1, 1])
cube(0, -1, walls = [1, 0, 1, 0, 1, 1])
cube(0, -2, walls = [1, 1, 0, 0, 1, 1])
cube(1, -2, walls = [1, 1, 0, 1, 0, 1])
cube(2, -2, walls = [0, 1, 1, 1, 0, 1])
# pit
cube(2, -2, -1, walls = [0, 1, 1, 1, 1, 0])
cube(2, -2, -2, walls = [0, 1, 1, 1, 1, 0])
cube(2, -2, -3, walls = [0, 1, 1, 1, 1, 0])

# bottom map
cube(0, 0, -4,walls = [1, 0, 1, 1, 1, 1])
cube(0, -1, -4, walls = [1, 0, 1, 0, 1, 1])
cube(0, -2, -4, walls = [1, 1, 0, 0, 1, 1])
cube(1, -2, -4, walls = [1, 1, 0, 1, 0, 1])
cube(2, -2, -4, walls = [1, 1, 1, 1, 0, 0])


scene2.addModel(importers.LoadXModel('meshes/soldier.x', scale = 1.0, 
    swizzler = lambda x: [x[0], x[1], -x[2]],
    n_flips = [-1,-1,-1],
    position= Vector3(0.0, 0.0, 0.0), geom = Geom.Cuboid(0.2, 2.0, 0.2)))

# add lights (use the studio lighting)
lights = models.Empty(position = Vector3(0.0, 5.0, 0.0), scale = Vector3(0.2, -0.5, 0.2))
p, c, d, s = LIGHTS.studio(1.5, 0.5)
for i in range(MAX_LIGHTS):
    lights.addModel(Light(i, position = p[i], color = c[i], distance = d[i], specularity = s[i]))
#lights = models.Empty()
#lights.addModel(Light(0, position = Vector3(0., 0., 0.1), color = Vector3(1., 1., 1.), specularity = 0.5, distance = 2.0))
scene.addModel(lights)

@w.add_event_handler(events.MouseMove, state = settings)
def mousemovehandler(self, e):
    if (self.state.isDragging):
        mouse_pt = e.position
        mousex = e.position[0] - self.state.mouse[0]
        mousey = e.position[1] - self.state.mouse[1]
        # invoke rotation
        glPushMatrix()
        glLoadMatrixf(scene._Transform._data)
        glRotate(mousex * 0.5, *scene._Transform._data[0:3,1])
        glRotate(mousey * 0.5, *scene._Transform._data[0:3,0])
        # compensate roll
        glRotated(-math.atan2(-scene._Transform._data[1,0], scene._Transform._data[1,1]) * \
                RAD2DEG, *scene._Transform._data[0:3,2])
        # compute fwd from strf (only works if y is up)
        state.strf = Vector3(*scene._Transform._data[0:3,0])
        
        state.fwd.x = -state.strf.z
        state.fwd.z = state.strf.x
        scene._Transform = Matrix4(glGetDoublev(GL_MODELVIEW_MATRIX))
        glPopMatrix()
        self.state.mouse = mouse_pt
        
        
@w.add_event_handler(events.MouseClick, state = settings)
def mouseclick(self, e):
    self.state.isDragging = False
    if (e.button.left and e.button.down):
        self.state.isDragging = True
        self.state.mouse = e.position

@w.add_event_handler(events.KeyPress, state = settings)
def keypress(self, e):    
    if e.key == events.ESCAPE:
        sys.exit(0)
    elif e.key == 'S':
        self.state.AO *= -1
    elif e.key == 'X':
        self.state.AO_ONLY *= -1
    elif e.key == 'L':
        self.state.ALL_LIGHTS *= -1
    elif e.key == 'E':
        self.state.EDGE *= -1
    elif e.key == ',':
        self.state.distance *= 0.99
    elif e.key == '.':
        self.state.distance /= 0.99
    elif e.key == 'D':
        self.state.DOF *= -1
    elif e.key == 'B':
        self.state.bloom *= -1
    elif e.key == 'w':
        self.state.w = 1
    elif e.key == 'a':
        self.state.a = 1
    elif e.key == 's':
        self.state.s = 1
    elif e.key == 'd':
        self.state.d = 1
    elif e.key == ' ':
        if not self.state.jump:
            character.body.addForce((0,500,0))
            self.state.jump = True
    elif e.key == 'J':
        self.state.light_strength *= 0.9
    elif e.key == 'K':    
        self.state.light_strength /= 0.9
    elif e.key == 'U':
        self.state.light_dist *= 0.9
    elif e.key == 'I':    
        self.state.light_dist /= 0.9
    elif e.key == '\\':
        print self.state.__dict__
    elif e.key == 'n':
        self.state.NMAP *= -1
    elif e.key == 'R':
        self.state.RELIEF *= -1
    elif e.key == 'P':
        if Physics().paused:
            Physics().Play()
        else:
            Physics().Pause()
        

            
@w.add_event_handler(events.KeyUp, state = settings)
def keyup(self, e):
    if e.key == 'w':
        self.state.w = 0
    elif e.key == 'a':
        self.state.a = 0
    elif e.key == 's':
        self.state.s = 0
    elif e.key == 'd':
        self.state.d = 0
    elif e.key == ' ':
        self.state.jump = False

        
@w.add_event_handler(events.Render, state = settings)
def render(self, e):
    p = Vector3(*character.body.getPosition())
    scene2._Position = Vector3(0.0, -0.6, 0.0) - p

    
SPEED = 0.05
VEL = 1.0
def crap():
    p = Vector3(*character.body.getPosition())
    def positioning(diff):
        character.body.setPosition(p + inp * SPEED)
        
    def velociting(inp):
        Physics().setFriction(0)
        character.body.setLinearVel(inp * VEL)
        
    fn = velociting
    Physics().setFriction(1000)
    if abs(character.body.getLinearVel()[1]) < 0.1:
        if state.w:
            fn(Vector3(0.0, 0.0, 0.0) - state.fwd)
        if state.s:
            fn(Vector3(0.0, 0.0, 0.0) + state.fwd)

        if state.a:
            fn(Vector3(0.0, 0.0, 0.0) - state.strf)
        if state.d:
            fn(Vector3(0.0, 0.0, 0.0) + state.strf)
        
    character.body.setAngularVel((0., 0., 0.))
    character.body.setRotation((1,0,0,0,1,0,0,0,1))
Physics().HACK(crap)

e.run()
